package cn.richard.demo.drools.starter;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map.Entry;

import javax.annotation.PostConstruct;

import org.drools.decisiontable.InputType;
import org.drools.decisiontable.SpreadsheetCompiler;
import org.kie.api.KieBase;
import org.kie.api.builder.Message;
import org.kie.api.builder.Results;
import org.kie.api.io.ResourceType;
import org.kie.api.runtime.KieSession;
import org.kie.internal.io.ResourceFactory;
import org.kie.internal.utils.KieHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;

@Slf4j
public class KieUtilService {
	
	@Autowired
	private AppDroolsConfigProperties appDroolsConfigProperties;
	
//	private KieHelper kieHelper;
	private KieBase kieBase;

	private final SpreadsheetCompiler compiler = new SpreadsheetCompiler();
	private final ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();

	@PostConstruct
	private void init() {
		reload();
	}

	/**
	 * 重新加载规则
	 */
	public void reload() {
		log.info(">> reload rules");
		LoadTypeEnum loadTypeE = LoadTypeEnum.valueOf(appDroolsConfigProperties.getLoadType().toUpperCase());
		KieHelper kieHelper = new KieHelper();
		log.info("规则加载方式：{}", loadTypeE.name());
		try {
			if(loadTypeE == LoadTypeEnum.CP) {
				Resource[] files = resourcePatternResolver.getResources("classpath*:" + appDroolsConfigProperties.getCpDir() + "/*.drl");
				if(ArrayUtil.isEmpty(files)){
					log.warn("目标类路径下未找到相关规则资源！");
				}else {
					log.info("类路径下规则文件列表：{}", files.toString());
					String path = null;
					for (Resource file : files) {
						path = appDroolsConfigProperties.getCpDir() + "/" + file.getFilename();
						kieHelper.addResource(ResourceFactory.newClassPathResource(path, "UTF-8"));
					}
				}
			}else if(loadTypeE == LoadTypeEnum.XLS) {
				log.info("配置的xls决策表文件列表：{}", appDroolsConfigProperties.getXlsFilePaths().toString());
				for (String path : appDroolsConfigProperties.getXlsFilePaths()) {
					InputStream is = new FileInputStream(new File(path));
					String drl = compiler.compile(is, InputType.XLS);
					//log.info("{} to compile drl：\n{}", path, drl);
					kieHelper.addContent(drl, ResourceType.DRL);
				}
			}
		}catch (IOException e) {
			log.error("规则文件加载异常", e);
			throw new RuntimeException("规则文件加载异常", e);
		}
		// drl文本语法校验
		verifyDrl(kieHelper);

		this.kieBase = kieHelper.build();
		log.info("<< reload rules");
	}

	/**
	 * 触发规则
	 * 
	 * @param fact
	 */
	public void fire(Object fact) {
		this.fire(fact, new FireOption().setFireLimit(-1));
	}

	/**
	 * 触发规则，指定触发配置
	 * 
	 * @param fact
	 * @param fireLimit
	 */
	public void fire(Object fact, FireOption option) {
		log.info(">> fire rules, fact:{}, option:{}", fact, option);
		KieSession session = kieBase.newKieSession();
		// Drools全局变量
		if (CollUtil.isNotEmpty(option.getGlobalVariables())) {
			for (Entry<String, Object> entry : option.getGlobalVariables().entrySet()) {
				session.setGlobal(entry.getKey(), entry.getValue());
			}
		}
		// fact放入工作内容
		session.insert(fact);
		// 指定聚焦议程组
		if (StrUtil.isNotBlank(option.getAgendaGroup())) {
			session.getAgenda().getAgendaGroup(option.getAgendaGroup()).setFocus();
		}
		// 触发数量配置
		int fireLimit = option.getFireLimit() == null ? -1 : option.getFireLimit();
		int count = session.fireAllRules(fireLimit);
		// 方法级KieSession
		session.dispose(); 
		
		log.info("<< fire rules, fact:{}, fireCount: {}", fact, count);
	}

	private void verifyDrl(KieHelper kieHelper) {
		Results verify = kieHelper.verify();
		if (verify.hasMessages(Message.Level.ERROR)) {
			List<Message> messages = verify.getMessages(Message.Level.ERROR);
			for (Message message : messages) {
				log.error("drl content verify error messages: " + message.getText());
			}
			throw new IllegalStateException("Compilation errors were found. Check the logs. One error is " + messages.get(0).getText());
		}
	}
}
